//
//Copyright 2001-2004 Stottler Henke Associates, Inc.
//All Rights Reserved.
////////////////////////////////////////////////////////////////////////////////

package com.stottlerhenke.simbionic.engine.core;

import java.util.*;

import com.stottlerhenke.simbionic.api.*;
import com.stottlerhenke.simbionic.common.parser.*;
import com.stottlerhenke.simbionic.common.*;
import com.stottlerhenke.simbionic.engine.SB_SingletonBook;
import com.stottlerhenke.simbionic.engine.file.SB_StreamReader;
import com.stottlerhenke.simbionic.engine.parser.SB_Variable;

/**
 * This class wraps a collection of variable bindings.
 */
public class SB_Bindings  
{
  protected ArrayList _bindings = new ArrayList();
  protected int _firstParam;

  public SB_Bindings() {
  }
  
  public void load(SB_StreamReader reader,SB_SingletonBook book)
		throws SB_FileException
	{
	  // read number of bindings
	  int num = reader.readInt();

	  SB_VarBinding binding;
	  for (int i = 0; i < num; i++) 
	  {
			int kind = reader.readInt();
			
			boolean classVar = (kind & EBindType.kClassFlag) == EBindType.kClassFlag;
			
			if( classVar  )
			{
				kind = kind ^ EBindType.kClassFlag;
				binding = new SB_VarClassBinding(kind);
			}
			else
				binding = new SB_VarBinding(kind);
				
	    binding.load(reader,book);
	    _bindings.add(binding);
	  }
  }


  public void initialize(SB_SingletonBook book)
  {
    for (int i = 0; i < _bindings.size(); i++)
    {
      ((SB_VarBinding)_bindings.get(i)).initialize(book);
    }

    _firstParam = _bindings.size();
  }

  /**
   * Replaces all instances of SB_Function that match the name of newMethod.
   * @param newMethod the method with which to replace existing methods
   */
  public void replaceFunction(SB_Function newFunction)
  {
	  for (int i = 0; i < _bindings.size(); i++)
	  {
	    ((SB_VarBinding)_bindings.get(i)).replaceFunction(newFunction);
	  }
  }
  
  /**
   * Set all binding to be in only. Used for class methods as actions
   */
  public void initParamBindings()
	{
    for (int i = 0; i < _bindings.size(); i++)
    {
      EParamDir dir = EParamDir.kNone;
      SB_VarBinding it = (SB_VarBinding)_bindings.get(i);
      if (it.GetBindingType() == EBindType.kBehaviorParam || it.GetBindingType() == EBindType.kActionParam)
      {
         dir = EParamDir.kIn;
         it.InitParamBinding(dir);
       }
     }
    
    SortBindings(); //They still need to be sorted, though somewhat differently
  }
  
  /**
   * This sort bindings depends upon the parameters being already specified int
   * the desired order. It just moves them to the end and sets up the _firstParam 
   * variable.
   * 
   * For use in the case of class methods where the parameter list is not known.
   */
  public void SortBindings()
  {
    int numParams = 0;
    int count = _bindings.size();
    for( int x = 0; x < count; x++ )
    {
      SB_VarBinding it = (SB_VarBinding)_bindings.get(x);
      if( !it.IsVarBinding() )
        numParams++;
    }

    // sort the bindings so all parameter bindings are at the end,
    // in the same order as the parameter list
    for (int i=0; i < numParams; i++)
    {
      for (int j = 0; j < _bindings.size(); j++)
      {
        SB_VarBinding it = (SB_VarBinding)_bindings.get(j);
        if (!it.IsVarBinding())
        {
          SB_VarBinding moved = it;
          _bindings.remove(it);
          _bindings.add(moved);
          break;
        }
      }
    }

    // mark the first parameter so we can always jump right to it
    _firstParam =  _bindings.size() - numParams;
  }
  
  public void initParamBindings(SB_ParamList params)
	{
    for (int i = 0; i < _bindings.size(); i++)
    {
      EParamDir dir = EParamDir.kNone;
      SB_VarBinding it = (SB_VarBinding)_bindings.get(i);
      if (it.GetBindingType() == EBindType.kBehaviorParam || it.GetBindingType() == EBindType.kActionParam)
      {
         // is there a param that matches the left-hand side of this binding?
         SB_Parameter param = params.GetParam(it.GetBoundName() );
         dir = param.GetDirection();
         it.InitParamBinding(dir);
       }
     }

     SortBindings(params);
  }

  public void SortBindings(SB_ParamList params){
    int numParams = params.GetNumParams();

    // sort the bindings so all parameter bindings are at the end,
    // in the same order as the parameter list
    for (int i=0; i < numParams; i++){
      SB_Parameter param = params.GetParam(i);

      // find this param in the binding list (it must be present since
      // all params must be bound exactly once)
      for (int j = 0; j < _bindings.size(); j++){
        SB_VarBinding it = (SB_VarBinding)_bindings.get(j);
        if (!it.IsVarBinding() && it.GetBoundName().compareTo(param.GetName()) == 0){
          SB_VarBinding moved = it;
          _bindings.remove(it);
          _bindings.add(moved);
          break;
        }
      }
    }

    // mark the first parameter so we can always jump right to it
    //_firstParam = (SB_VarBinding)_bindings.get(0 + (_bindings.size() - numParams));
    _firstParam =  _bindings.size() - numParams;
  }

  public void ApplyForVariables(SB_Entity entity,SB_ExecutionFrame contextFrame)  throws SB_Exception {
    for (int i = 0; i != _firstParam/*_bindings.size()*/; i++)
    {
      ((SB_VarBinding)_bindings.get(i)).Apply(entity,contextFrame, contextFrame.GetLogger() );
    }
  }

  public void ApplyForInvocation(SB_Entity entity, SB_ExecutionFrame contextFrame, SB_ExecutionFrame newFrame)  throws SB_Exception {
    for ( int i = _firstParam; i < _bindings.size(); i++){
      // don't waste time binding "out" parameters on behavior invocation
      SB_VarBinding it = (SB_VarBinding)_bindings.get(i);
      if (it.IsInBinding()){
        it.Apply(entity,contextFrame,newFrame, newFrame.GetLogger());
      }
    }
  }

  public void ApplyForInvocation(SB_Entity entity, SB_ExecutionFrame contextFrame, ArrayList args )  throws SB_Exception {
  // action parameter bindings are already sorted in the correct order
  SB_Variable argIt;
  for (int i = _firstParam; i < _bindings.size(); i++) {
    // bind the "out" parameters to ensure that parameters of the correct
    // type get created and passed to the action, even though the value of
    // those parameters is not important
    argIt = ((SB_VarBinding) _bindings.get(i)).ApplyTo(entity, contextFrame, contextFrame.GetLogger());
    args.add(argIt);
  }
 }

  public void ApplyForReturn(SB_Entity entity,SB_ExecutionFrame contextFrame, SB_ExecutionFrame doneFrame) throws SB_Exception {
    SB_VarBinding it;
    for (int i = _firstParam; i < _bindings.size(); i++){
      // make sure we don't bind "in" parameters or bindings that don't
      // have variables on the right-hand side
      it = (SB_VarBinding)_bindings.get(i);
      if (it.IsOutBinding()){
        it.ReverseApply(entity,contextFrame,doneFrame, contextFrame.GetLogger());
      }
    }
  }

  public void ApplyForReturn(SB_Entity entity,SB_ExecutionFrame contextFrame, ArrayList params){
    SB_Variable paramIt;
    SB_VarBinding it;
    //for(int i = 0; i < params.size(); i++){
    int i = 0;
    for (int j = _firstParam; j < _bindings.size(); j++) {
      paramIt = (SB_Variable)params.get(i);
      it = (SB_VarBinding)_bindings.get(j);
      // make sure we don't bind "in" parameters or bindings that don't
      // have variables on the right-hand side
      if (it.IsOutBinding()) {
        it.ReverseApply(entity, contextFrame, paramIt, contextFrame.GetLogger());
      }
      ++i;
    }
    //}
  }

}